diff --git a/.changelog/3522.txt b/.changelog/3522.txt new file mode 100644 index 0000000000..166673f781 --- /dev/null +++ b/.changelog/3522.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +tencentcloud_mysql_audit_service +``` \ No newline at end of file diff --git a/tencentcloud/provider.go b/tencentcloud/provider.go index 7aca6e82d1..139913345d 100644 --- a/tencentcloud/provider.go +++ b/tencentcloud/provider.go @@ -1460,6 +1460,7 @@ func Provider() *schema.Provider { "tencentcloud_mysql_switch_proxy": cdb.ResourceTencentCloudMysqlSwitchProxy(), "tencentcloud_mysql_ssl": cdb.ResourceTencentCloudMysqlSsl(), "tencentcloud_mysql_cls_log_attachment": cdb.ResourceTencentCloudMysqlClsLogAttachment(), + "tencentcloud_mysql_audit_service": cdb.ResourceTencentCloudMysqlAuditService(), "tencentcloud_cos_bucket": cos.ResourceTencentCloudCosBucket(), "tencentcloud_cos_bucket_object": cos.ResourceTencentCloudCosBucketObject(), "tencentcloud_cos_bucket_referer": cos.ResourceTencentCloudCosBucketReferer(), diff --git a/tencentcloud/provider.md b/tencentcloud/provider.md index b17431ed87..b56f090aa5 100644 --- a/tencentcloud/provider.md +++ b/tencentcloud/provider.md @@ -814,6 +814,7 @@ tencentcloud_mysql_isolate_instance tencentcloud_mysql_dr_instance tencentcloud_mysql_ssl tencentcloud_mysql_cls_log_attachment +tencentcloud_mysql_audit_service Cloud Monitor(Monitor) Data Source diff --git a/tencentcloud/services/cdb/resource_tc_mysql_audit_service.go b/tencentcloud/services/cdb/resource_tc_mysql_audit_service.go new file mode 100644 index 0000000000..9505431f96 --- /dev/null +++ b/tencentcloud/services/cdb/resource_tc_mysql_audit_service.go @@ -0,0 +1,343 @@ +package cdb + +import ( + "context" + "fmt" + "log" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + cdbv20170320 "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdb/v20170320" + + tccommon "github.com/tencentcloudstack/terraform-provider-tencentcloud/tencentcloud/common" + "github.com/tencentcloudstack/terraform-provider-tencentcloud/tencentcloud/internal/helper" + "github.com/tencentcloudstack/terraform-provider-tencentcloud/tencentcloud/ratelimit" +) + +func ResourceTencentCloudMysqlAuditService() *schema.Resource { + return &schema.Resource{ + Create: resourceTencentCloudMysqlAuditServiceCreate, + Read: resourceTencentCloudMysqlAuditServiceRead, + Update: resourceTencentCloudMysqlAuditServiceUpdate, + Delete: resourceTencentCloudMysqlAuditServiceDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + Schema: map[string]*schema.Schema{ + "instance_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "TencentDB for MySQL instance ID.", + }, + + "log_expire_day": { + Type: schema.TypeInt, + Required: true, + Description: "Retention period of the audit log. Valid values: `7` (one week), `30` (one month), `90` (three months), `180` (six months), `365` (one year), `1095` (three years), `1825` (five years).", + }, + + "high_log_expire_day": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + Description: "Retention period of high-frequency audit logs. Valid values: `7` (one week), `30` (one month).", + }, + + "rule_template_ids": { + Type: schema.TypeSet, + Optional: true, + Description: "Rule template ID. If both this parameter and AuditRuleFilters are not specified, all SQL statements will be recorded.", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + + "audit_all": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + Description: "Audit type. Valid values: true: Record all; false: Record by rules (default value).", + }, + }, + } +} + +func resourceTencentCloudMysqlAuditServiceCreate(d *schema.ResourceData, meta interface{}) error { + defer tccommon.LogElapsed("resource.tencentcloud_mysql_audit_service.create")() + defer tccommon.InconsistentCheck(d, meta)() + + var ( + logId = tccommon.GetLogId(tccommon.ContextNil) + ctx = tccommon.NewResourceLifeCycleHandleFuncContext(context.Background(), logId, d, meta) + request = cdbv20170320.NewOpenAuditServiceRequest() + instanceId string + ) + + if v, ok := d.GetOk("instance_id"); ok { + request.InstanceId = helper.String(v.(string)) + instanceId = v.(string) + } + + if v, ok := d.GetOkExists("log_expire_day"); ok { + request.LogExpireDay = helper.IntUint64(v.(int)) + } + + if v, ok := d.GetOkExists("high_log_expire_day"); ok { + request.HighLogExpireDay = helper.IntUint64(v.(int)) + } + + if v, ok := d.GetOk("rule_template_ids"); ok { + ruleTemplateIdsSet := v.(*schema.Set).List() + for i := range ruleTemplateIdsSet { + if ruleTemplateId, ok := ruleTemplateIdsSet[i].(string); ok && ruleTemplateId != "" { + request.RuleTemplateIds = append(request.RuleTemplateIds, helper.String(ruleTemplateId)) + } + } + } + + if v, ok := d.GetOkExists("audit_all"); ok { + request.AuditAll = helper.Bool(v.(bool)) + } + + reqErr := resource.Retry(tccommon.WriteRetryTimeout, func() *resource.RetryError { + result, e := meta.(tccommon.ProviderMeta).GetAPIV3Conn().UseMysqlClient().OpenAuditServiceWithContext(ctx, 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 reqErr != nil { + log.Printf("[CRITAL]%s create mysql audit service failed, reason:%+v", logId, reqErr) + return reqErr + } + + d.SetId(instanceId) + + // wait + waitRequest := cdbv20170320.NewDescribeAuditInstanceListRequest() + waitRequest.Filters = []*cdbv20170320.AuditInstanceFilters{ + { + Name: helper.String("InstanceId"), + ExactMatch: helper.Bool(true), + Values: helper.Strings([]string{instanceId}), + }, + } + + reqErr = resource.Retry(tccommon.ReadRetryTimeout*5, func() *resource.RetryError { + ratelimit.Check(request.GetAction()) + result, e := meta.(tccommon.ProviderMeta).GetAPIV3Conn().UseMysqlClient().DescribeAuditInstanceListWithContext(ctx, waitRequest) + if e != nil { + return tccommon.RetryError(e) + } else { + log.Printf("[DEBUG]%s api[%s] success, request body [%s], response body [%s]\n", logId, waitRequest.GetAction(), waitRequest.ToJsonString(), result.ToJsonString()) + } + + if result == nil || result.Response == nil || result.Response.Items == nil || len(result.Response.Items) == 0 { + return resource.RetryableError(fmt.Errorf("Describe audit instance list failed, Response is nil.")) + } + + if len(result.Response.Items) != 1 { + return resource.RetryableError(fmt.Errorf("Describe audit instance list failed, more than one instance item found.")) + } + + item := result.Response.Items[0] + if item.AuditStatus != nil && *item.AuditStatus == "ON" { + if item.AuditTask != nil && *item.AuditTask == 0 { + return nil + } + } + + return resource.RetryableError(fmt.Errorf("waiting for mysql [%s] audit service opening", instanceId)) + }) + + if reqErr != nil { + return reqErr + } + + return resourceTencentCloudMysqlAuditServiceRead(d, meta) +} + +func resourceTencentCloudMysqlAuditServiceRead(d *schema.ResourceData, meta interface{}) error { + defer tccommon.LogElapsed("resource.tencentcloud_mysql_audit_service.read")() + defer tccommon.InconsistentCheck(d, meta)() + + var ( + logId = tccommon.GetLogId(tccommon.ContextNil) + ctx = tccommon.NewResourceLifeCycleHandleFuncContext(context.Background(), logId, d, meta) + service = MysqlService{client: meta.(tccommon.ProviderMeta).GetAPIV3Conn()} + instanceId = d.Id() + ) + + respData, err := service.DescribeMysqlAuditInstanceListById(ctx, instanceId) + if err != nil { + return err + } + + if respData == nil { + log.Printf("[WARN]%s resource `tencentcloud_mysql_audit_service` [%s] not found, please check if it has been deleted.\n", logId, d.Id()) + d.SetId("") + return nil + } + + if respData.InstanceId != nil { + _ = d.Set("instance_id", respData.InstanceId) + } + + if respData.LogExpireDay != nil { + _ = d.Set("log_expire_day", respData.LogExpireDay) + } + + if respData.HighLogExpireDay != nil { + _ = d.Set("high_log_expire_day", respData.HighLogExpireDay) + } + + if respData.RuleTemplateIds != nil { + _ = d.Set("rule_template_ids", respData.RuleTemplateIds) + } + + if respData.AuditAll != nil { + _ = d.Set("audit_all", respData.AuditAll) + } + + return nil +} + +func resourceTencentCloudMysqlAuditServiceUpdate(d *schema.ResourceData, meta interface{}) error { + defer tccommon.LogElapsed("resource.tencentcloud_mysql_audit_service.update")() + defer tccommon.InconsistentCheck(d, meta)() + + var ( + logId = tccommon.GetLogId(tccommon.ContextNil) + ctx = tccommon.NewResourceLifeCycleHandleFuncContext(context.Background(), logId, d, meta) + instanceId = d.Id() + ) + + needChange := false + mutableArgs := []string{"log_expire_day", "high_log_expire_day", "rule_template_ids", "audit_all"} + for _, v := range mutableArgs { + if d.HasChange(v) { + needChange = true + break + } + } + + if needChange { + request := cdbv20170320.NewModifyAuditServiceRequest() + if v, ok := d.GetOkExists("log_expire_day"); ok { + request.LogExpireDay = helper.IntUint64(v.(int)) + } + + if v, ok := d.GetOkExists("high_log_expire_day"); ok { + request.HighLogExpireDay = helper.IntUint64(v.(int)) + } + + if v, ok := d.GetOk("rule_template_ids"); ok { + ruleTemplateIdsSet := v.(*schema.Set).List() + for i := range ruleTemplateIdsSet { + if ruleTemplateId, ok := ruleTemplateIdsSet[i].(string); ok && ruleTemplateId != "" { + request.RuleTemplateIds = append(request.RuleTemplateIds, helper.String(ruleTemplateId)) + } + } + } + + if v, ok := d.GetOkExists("audit_all"); ok { + request.AuditAll = helper.Bool(v.(bool)) + } + + request.InstanceId = &instanceId + reqErr := resource.Retry(tccommon.WriteRetryTimeout, func() *resource.RetryError { + result, e := meta.(tccommon.ProviderMeta).GetAPIV3Conn().UseMysqlClient().ModifyAuditServiceWithContext(ctx, 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 reqErr != nil { + log.Printf("[CRITAL]%s update mysql audit service failed, reason:%+v", logId, reqErr) + return reqErr + } + } + + return resourceTencentCloudMysqlAuditServiceRead(d, meta) +} + +func resourceTencentCloudMysqlAuditServiceDelete(d *schema.ResourceData, meta interface{}) error { + defer tccommon.LogElapsed("resource.tencentcloud_mysql_audit_service.delete")() + defer tccommon.InconsistentCheck(d, meta)() + + var ( + logId = tccommon.GetLogId(tccommon.ContextNil) + ctx = tccommon.NewResourceLifeCycleHandleFuncContext(context.Background(), logId, d, meta) + request = cdbv20170320.NewCloseAuditServiceRequest() + instanceId = d.Id() + ) + + request.InstanceId = &instanceId + reqErr := resource.Retry(tccommon.WriteRetryTimeout, func() *resource.RetryError { + result, e := meta.(tccommon.ProviderMeta).GetAPIV3Conn().UseMysqlClient().CloseAuditServiceWithContext(ctx, 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 reqErr != nil { + log.Printf("[CRITAL]%s delete mysql audit service failed, reason:%+v", logId, reqErr) + return reqErr + } + + // wait + waitRequest := cdbv20170320.NewDescribeAuditInstanceListRequest() + waitRequest.Filters = []*cdbv20170320.AuditInstanceFilters{ + { + Name: helper.String("InstanceId"), + ExactMatch: helper.Bool(true), + Values: helper.Strings([]string{instanceId}), + }, + } + + reqErr = resource.Retry(tccommon.ReadRetryTimeout*5, func() *resource.RetryError { + ratelimit.Check(request.GetAction()) + result, e := meta.(tccommon.ProviderMeta).GetAPIV3Conn().UseMysqlClient().DescribeAuditInstanceListWithContext(ctx, waitRequest) + if e != nil { + return tccommon.RetryError(e) + } else { + log.Printf("[DEBUG]%s api[%s] success, request body [%s], response body [%s]\n", logId, waitRequest.GetAction(), waitRequest.ToJsonString(), result.ToJsonString()) + } + + if result == nil || result.Response == nil || result.Response.Items == nil || len(result.Response.Items) == 0 { + return resource.RetryableError(fmt.Errorf("Describe audit instance list failed, Response is nil.")) + } + + if len(result.Response.Items) != 1 { + return resource.RetryableError(fmt.Errorf("Describe audit instance list failed, more than one instance item found.")) + } + + item := result.Response.Items[0] + if item.AuditStatus != nil && *item.AuditStatus == "OFF" { + if item.AuditTask != nil && *item.AuditTask == 0 { + return nil + } + } + + return resource.RetryableError(fmt.Errorf("waiting for mysql [%s] audit service closing", instanceId)) + }) + + if reqErr != nil { + return reqErr + } + + return nil +} diff --git a/tencentcloud/services/cdb/resource_tc_mysql_audit_service.md b/tencentcloud/services/cdb/resource_tc_mysql_audit_service.md new file mode 100644 index 0000000000..3021f91d8c --- /dev/null +++ b/tencentcloud/services/cdb/resource_tc_mysql_audit_service.md @@ -0,0 +1,36 @@ +Provides a resource to create a Mysql audit service + +Example Usage + +If audit_all is true + +```hcl +resource "tencentcloud_mysql_audit_service" "example" { + instance_id = "cdb-3kwa3gfj" + log_expire_day = 30 + high_log_expire_day = 7 + audit_all = true +} +``` + +If audit_all is false + +```hcl +resource "tencentcloud_mysql_audit_service" "example" { + instance_id = "cdb-3kwa3gfj" + log_expire_day = 30 + high_log_expire_day = 7 + rule_template_ids = [ + "cdb-art-3a9ww0oj" + ] + audit_all = false +} +``` + +Import + +Mysql audit service can be imported using the id, e.g. + +``` +terraform import tencentcloud_mysql_audit_service.example cdb-3kwa3gfj +``` diff --git a/tencentcloud/services/cdb/resource_tc_mysql_audit_service_test.go b/tencentcloud/services/cdb/resource_tc_mysql_audit_service_test.go new file mode 100644 index 0000000000..10ba62addd --- /dev/null +++ b/tencentcloud/services/cdb/resource_tc_mysql_audit_service_test.go @@ -0,0 +1,68 @@ +package cdb_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + tcacctest "github.com/tencentcloudstack/terraform-provider-tencentcloud/tencentcloud/acctest" +) + +func TestAccTencentCloudMysqlAuditServiceResource_basic(t *testing.T) { + t.Parallel() + resource.Test(t, resource.TestCase{ + PreCheck: func() { + tcacctest.AccPreCheck(t) + }, + Providers: tcacctest.AccProviders, + Steps: []resource.TestStep{ + { + Config: testAccMysqlAuditService, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("tencentcloud_mysql_audit_service.example", "id"), + resource.TestCheckResourceAttrSet("tencentcloud_mysql_audit_service.example", "instance_id"), + resource.TestCheckResourceAttrSet("tencentcloud_mysql_audit_service.example", "log_expire_day"), + resource.TestCheckResourceAttrSet("tencentcloud_mysql_audit_service.example", "high_log_expire_day"), + resource.TestCheckResourceAttrSet("tencentcloud_mysql_audit_service.example", "rule_template_ids"), + resource.TestCheckResourceAttrSet("tencentcloud_mysql_audit_service.example", "audit_all"), + ), + }, + { + Config: testAccMysqlAuditServiceUpdate, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("tencentcloud_mysql_audit_service.example", "id"), + resource.TestCheckResourceAttrSet("tencentcloud_mysql_audit_service.example", "instance_id"), + resource.TestCheckResourceAttrSet("tencentcloud_mysql_audit_service.example", "log_expire_day"), + resource.TestCheckResourceAttrSet("tencentcloud_mysql_audit_service.example", "high_log_expire_day"), + resource.TestCheckResourceAttrSet("tencentcloud_mysql_audit_service.example", "audit_all"), + ), + }, + { + ResourceName: "tencentcloud_mysql_audit_service.example", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +const testAccMysqlAuditService = ` +resource "tencentcloud_mysql_audit_service" "example" { + instance_id = "cdb-3kwa3gfj" + log_expire_day = 90 + high_log_expire_day = 30 + rule_template_ids = [ + "cdb-art-3a9ww0oj" + ] + audit_all = false +} +` + +const testAccMysqlAuditServiceUpdate = ` +resource "tencentcloud_mysql_audit_service" "example" { + instance_id = "cdb-3kwa3gfj" + log_expire_day = 30 + high_log_expire_day = 7 + audit_all = true +} +` diff --git a/tencentcloud/services/cdb/service_tencentcloud_mysql.go b/tencentcloud/services/cdb/service_tencentcloud_mysql.go index 65080cfa26..20e22e31c7 100644 --- a/tencentcloud/services/cdb/service_tencentcloud_mysql.go +++ b/tencentcloud/services/cdb/service_tencentcloud_mysql.go @@ -3529,3 +3529,51 @@ func (me *MysqlService) DeleteMysqlDatabaseById(ctx context.Context, instanceId return } + +func (me *MysqlService) DescribeMysqlAuditInstanceListById(ctx context.Context, instanceId string) (ret *cdb.InstanceDbAuditStatus, errRet error) { + logId := tccommon.GetLogId(ctx) + + request := cdb.NewDescribeAuditInstanceListRequest() + response := cdb.NewDescribeAuditInstanceListResponse() + request.Filters = []*cdb.AuditInstanceFilters{ + { + Name: helper.String("InstanceId"), + ExactMatch: helper.Bool(true), + Values: helper.Strings([]string{instanceId}), + }, + } + + defer func() { + if errRet != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", logId, request.GetAction(), request.ToJsonString(), errRet.Error()) + } + }() + + errRet = resource.Retry(tccommon.ReadRetryTimeout, func() *resource.RetryError { + ratelimit.Check(request.GetAction()) + result, e := me.client.UseMysqlClient().DescribeAuditInstanceList(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()) + } + + if result == nil || result.Response == nil || result.Response.Items == nil || len(result.Response.Items) == 0 { + return resource.RetryableError(fmt.Errorf("Describe audit instance list failed, Response is nil.")) + } + + response = result + return nil + }) + + if errRet != nil { + return + } + + if len(response.Response.Items) != 1 { + return nil, fmt.Errorf("Describe audit instance list failed, Response items count is not 1.") + } + + ret = response.Response.Items[0] + return +} diff --git a/website/docs/r/mysql_audit_service.html.markdown b/website/docs/r/mysql_audit_service.html.markdown new file mode 100644 index 0000000000..6b58e37deb --- /dev/null +++ b/website/docs/r/mysql_audit_service.html.markdown @@ -0,0 +1,66 @@ +--- +subcategory: "TencentDB for MySQL(cdb)" +layout: "tencentcloud" +page_title: "TencentCloud: tencentcloud_mysql_audit_service" +sidebar_current: "docs-tencentcloud-resource-mysql_audit_service" +description: |- + Provides a resource to create a Mysql audit service +--- + +# tencentcloud_mysql_audit_service + +Provides a resource to create a Mysql audit service + +## Example Usage + +### If audit_all is true + +```hcl +resource "tencentcloud_mysql_audit_service" "example" { + instance_id = "cdb-3kwa3gfj" + log_expire_day = 30 + high_log_expire_day = 7 + audit_all = true +} +``` + +### If audit_all is false + +```hcl +resource "tencentcloud_mysql_audit_service" "example" { + instance_id = "cdb-3kwa3gfj" + log_expire_day = 30 + high_log_expire_day = 7 + rule_template_ids = [ + "cdb-art-3a9ww0oj" + ] + audit_all = false +} +``` + +## Argument Reference + +The following arguments are supported: + +* `instance_id` - (Required, String, ForceNew) TencentDB for MySQL instance ID. +* `log_expire_day` - (Required, Int) Retention period of the audit log. Valid values: `7` (one week), `30` (one month), `90` (three months), `180` (six months), `365` (one year), `1095` (three years), `1825` (five years). +* `audit_all` - (Optional, Bool) Audit type. Valid values: true: Record all; false: Record by rules (default value). +* `high_log_expire_day` - (Optional, Int) Retention period of high-frequency audit logs. Valid values: `7` (one week), `30` (one month). +* `rule_template_ids` - (Optional, Set: [`String`]) Rule template ID. If both this parameter and AuditRuleFilters are not specified, all SQL statements will be recorded. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - ID of the resource. + + + +## Import + +Mysql audit service can be imported using the id, e.g. + +``` +terraform import tencentcloud_mysql_audit_service.example cdb-3kwa3gfj +``` + diff --git a/website/tencentcloud.erb b/website/tencentcloud.erb index 244e028921..79209dd389 100644 --- a/website/tencentcloud.erb +++ b/website/tencentcloud.erb @@ -6060,6 +6060,9 @@